import { Reducer } from "redux";
import { ADD_COMPONENT, SET_CURRENT_ELEMENT, SET_CURRENT_ELEMENT_PROPS, ADJUST_COMPONENT_SORT, SET_PAGE_PROPS, UNDO_OPERATE, REDO_OPERATE, DELETE_LAYER } from "../constants";
import { defaultPageStyle } from "common/data/constants";
import { PageStyleProps } from "common/data/types";
import { cloneDeep } from "lodash";
import { IAction } from "../type";

export interface IComponent {
    name: string;  // 对应业务组件的名称
    id: string;  // uuid生成的页面元素的唯一标示
    index: number;  // 加入组件的顺序
    props: { [key: string]: any }
}

interface IOperateProps {
    id: string;
    diff: {[key: string]: any};
}

export interface IEditorProps {
    components: IComponent[];  // 编辑页面的组件信息
    currentElement: string;  // 当前正在编辑的组件id
    defaultPageStyle: PageStyleProps;
    operateList: IOperateProps[];  // 执行操作的所有属性队列，用于执行撤销操作时进行恢复
    cursor: number;  // 记录一个游标，用于定位当前恢复的步骤位置
}

const defaultEditorData: IEditorProps = {
    components: [],
    currentElement: "",
    defaultPageStyle: defaultPageStyle,
    operateList: [],
    cursor: 0
}

const editorReducer: Reducer<IEditorProps, IAction> = (state = defaultEditorData, action) => {
    switch(action.type){
        case ADD_COMPONENT:
            return {
                ...state,
                components: [...state.components, action.payload]
            }
        case SET_CURRENT_ELEMENT:
            return {
                ...state,
                currentElement: action.payload
            }
        case SET_CURRENT_ELEMENT_PROPS:  // 设置当前组件属性
            const currentIndex = state.components.findIndex(comp => comp.id === state.currentElement);
            if(currentIndex !== -1){
                const components = state.components;
                const operateList = state.operateList;
                const oldComponentsProps = cloneDeep(components[currentIndex].props);
                components[currentIndex] = {
                    ...components[currentIndex],
                    props: {
                        ...components[currentIndex].props,
                        ...action.payload
                    }
                }
                const diff = compareObjectProps(components[currentIndex].props, oldComponentsProps);
                let needCacheOperate = false;
                if(JSON.stringify(diff) !== "{}"){
                    operateList.splice(state.cursor, 0, {
                        id: components[currentIndex].id,
                        diff
                    });
                    operateList.splice(state.cursor + 1, operateList.length - state.cursor)
                    if(operateList.length > 100){
                        operateList.shift();  // 最多缓存100步操作
                    }
                    needCacheOperate = true;
                }
                return {
                    ...state,
                    components: [...components],
                    operateList: operateList,
                    cursor: needCacheOperate ? operateList.length : state.cursor
                }
            }
            return state;
        case ADJUST_COMPONENT_SORT:
            return {
                ...state,
                components: action.payload
            }
        case SET_PAGE_PROPS:
            return {
                ...state,
                defaultPageStyle: {
                    ...state.defaultPageStyle,
                    ...action.payload
                }
            }
        case UNDO_OPERATE:  // 撤销操作
            if(state.cursor === 0) return state;
            const undoOperate = state.operateList[state.cursor - 1];
            const undoDiffIndex = state.components.findIndex(comp => comp.id === undoOperate.id);
            if(undoDiffIndex !== -1){
                const components = state.components;
                const undoDiff = compareObjectProps(undoOperate.diff, components[undoDiffIndex].props);
                state.operateList.splice(state.cursor - 1, 1, {
                    id: undoOperate.id,
                    diff: undoDiff
                });
                components[undoDiffIndex] = {
                    ...components[undoDiffIndex],
                    props: {
                        ...components[undoDiffIndex].props,
                        ...undoOperate.diff
                    }
                }
                return {
                    ...state,
                    components: [...components],
                    cursor: state.cursor - 1
                }
            }
            return state;
        case REDO_OPERATE:  // 执行重做操作
            if(state.cursor >= state.operateList.length) return state;
            const redoOperate = state.operateList[state.cursor];
            const redoDiffIndex = state.components.findIndex(comp => comp.id === redoOperate.id);
            if(redoDiffIndex !== -1){
                const components = state.components;
                const redoDiff = compareObjectProps(redoOperate.diff, components[redoDiffIndex].props);
                state.operateList.splice(state.cursor, 1, {
                    id: redoOperate.id,
                    diff: redoDiff
                });
                components[redoDiffIndex] = {
                    ...components[redoDiffIndex],
                    props: {
                        ...components[redoDiffIndex].props,
                        ...redoOperate.diff
                    }
                }
                return {
                    ...state,
                    components: [...components],
                    cursor: state.cursor + 1
                }
            }
            return state;
        case DELETE_LAYER:  // 删除当前图层
            let movePos = 0;  // 计算在删除元素之后需要将cursor移动的位置
            const newOperate = state.operateList.filter((comp, index) => {
                if(comp.id === state.currentElement && index < state.cursor){
                    movePos++;
                }
                return comp.id !== state.currentElement
            })
            const compIndex = state.components.findIndex(comp => comp.id === state.currentElement);
            state.components.splice(compIndex, 1);
            return {
                ...state,
                components: [...state.components],
                operateList: newOperate,
                cursor: state.cursor - movePos >= 0 ? state.cursor - movePos : 0,
                currentElement: ""
            }
        default:
            return state;
    }
}

// 对比obj1和obj2两个对象的属性差异, 差异部分将已obj2为准
function compareObjectProps(obj1: {[key: string]: any}, obj2: {[key: string]: any}){
    const diff = {};
    Object.keys(obj1).forEach(key => {
        if(Object.prototype.hasOwnProperty.call(obj1, key)){
            const obj2Value = Reflect.get(obj2, key);
            if(!obj2Value) return;
            if(String.prototype.toLowerCase.call(obj1[key]) !== String.prototype.toLowerCase.call(obj2Value)){
                Reflect.set(diff, key, obj2Value);
            }
        }
    });
    Object.keys(obj2).forEach(key => {
        if(!Reflect.has(diff, key)){
            const obj1Value = Reflect.get(obj1, key);
            if(!obj1Value) return;
            if(String.prototype.toLowerCase.call(obj1[key]) !== String.prototype.toLowerCase.call(obj1Value)){
                Reflect.set(diff, key, obj1Value);
            }
        }
    })
    return diff;
}


export default editorReducer;